/*!
 * \file xml_core.h
 *
 * \brief XML模块主文件
 *
 * All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.
 *
 * \version 1.0
 * \author songbb
 * \date 2016/08/08
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <frservice_comm.h>
#include <frct_detect_hash.h>

#include "frlog.h"
#include "frreport_comm.h"

#include "frct_comm.h"

#include "xml_comm.h"
#include "xml_header.h"
#include "xml_packet.h"
#include "xml_trans.h"
#include "config.h"

extern void *g_p_apm_context;

/*******************************************************************************
*  FUNC     :  xml Session初始化
*  ARGS     :  *p_st_session                            (L7层session信息)
*  RTN      :  0                                        (FR_SUCC)
*              1                                        (FR_FAIL)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __xml_private_session_init(L7_session *p_st_session)
{
    XML_PRINT_INFO("session init\n");
    L7session_xml_private *p_st_xml_private = NULL;      // XML的私有信息
    L7session_xml *p_st_xml_sess = NULL;                 // XML的私有session信息
    L7_xml_packet *p_st_xml_packet = NULL;               // XML格式的packet包
    L7_xml_stmt *p_xml_stmt = NULL;                      // XML的statement信息结构

    // 1. 初始化SIP的私有session信息
    if ((p_st_xml_sess = (L7session_xml *) frmalloc(sizeof(L7session_xml))) == NULL)
    {
        XML_MALLOCK_ERR;
        goto error;
    }
    memset(p_st_xml_sess, 0x00, sizeof(L7session_xml));
    p_st_xml_sess->hdr_type = APM_XML_HDR_TYPE_INIT;
    p_st_xml_sess->i_hdrlen_from_head = FR_YES;

    // 2. 初始化xml格式的packet包
    if ((p_st_xml_packet = (L7_xml_packet *) frmalloc(sizeof(L7_xml_packet))) == NULL)
    {
        XML_MALLOCK_ERR;
        goto error;
    }
    memset(p_st_xml_packet, 0x00, sizeof(L7_xml_packet));
    if (xml_init_packet(p_st_xml_packet) != FR_SUCC)
        goto error;

    // 3. 初始化statement hash
    if ((p_xml_stmt = (L7_xml_stmt *) frmalloc(sizeof(L7_xml_stmt))) == NULL)
    {
        XML_MALLOCK_ERR;
        goto error;
    }
    memset(p_xml_stmt, 0x00, sizeof(L7_xml_stmt));

    // 4. 创建和初始化xml的私有信息
    if ((p_st_xml_private = (L7session_xml_private *) frmalloc(sizeof(L7session_xml_private))) == NULL)
    {
        XML_MALLOCK_ERR;
        goto error;
    }
    memset(p_st_xml_private, 0x00, sizeof(L7session_xml_private));
    p_st_xml_private->p_st_sess = p_st_xml_sess;
    p_st_xml_private->p_st_xml_packet = p_st_xml_packet;
    p_st_xml_private->p_st_session = p_st_session;
    p_st_xml_private->p_st_xml_stmt = p_xml_stmt;

    // 4. 设置L7_session的p_session_private信息
    frct_set_session_private(p_st_session, (void *)p_st_xml_private);

    return FR_SUCC;
error:
    if (p_st_xml_packet != NULL)
    {
        bytebuff_free(&(p_st_xml_packet->p_st_packet));
        xml_free((void **) &p_st_xml_packet);
    }
    xml_free((void **) &p_xml_stmt);
    xml_free((void **) &p_st_xml_private);
    return FR_FAIL;
}

FR_STATIC void __xml_destroy_session(L7session_xml **pp_st_session)
{
    if (pp_st_session == NULL || *pp_st_session == NULL)
        return;

    xml_free((void **) pp_st_session);
    return;
}

int xml_detect(Conntrack *p_st_conn
                , Apm_packet *p_analyze_in)
{
    XML_PRINT_INFO("xml_detect\n");
    L7session_xml *p_st_xml_sess = NULL;

#if APM_XML_DBG
    char arr_ip_src[56], arr_ip_dst[56];
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_analyze_in->src_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_src), "%s", int_ntoa(p_analyze_in->dst_ip));
    XML_PRINT_TIMEVAL("PacketIn", p_analyze_in->st_timestamp);
    XML_PRINT_DBG("Src:[%s:%d] Dest:[%s:%d] Length:[%u]\n", arr_ip_src, p_analyze_in->src_port, arr_ip_dst, p_analyze_in->dst_port, p_analyze_in->ui_payload_len);
#endif

    if ((p_st_xml_sess = (L7session_xml *) frmalloc(sizeof(L7session_xml))) == NULL)
    {
        XML_MALLOCK_ERR;
        goto unknown;
    }
    memset(p_st_xml_sess, 0x00, sizeof(L7session_xml));

    if (p_analyze_in->i_direction == FR_DIRECT_UNKNOWN || frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
    {
        XML_PRINT_CRIT("p_analyze_in->i_direction:[%u] frct_get_direct_known(p_st_conn):[%u]\n", p_analyze_in->i_direction, frct_get_direct_known(p_st_conn));
        goto unknown;
    }

    if (xml_header_detect(p_st_conn, p_analyze_in) != FR_SUCC)
        goto unknown;

    __xml_destroy_session(&p_st_xml_sess);
    XML_PRINT_INFO("xml_detect success\n\n");
    return APM_L7_IS_YES;
unknown:
    __xml_destroy_session(&p_st_xml_sess);
    XML_PRINT_INFO("xml_detect failed\n\n");
    return APM_L7_IS_UNKNOWN;
}

int xml_analyze(Conntrack *p_st_conn
                , Apm_packet *p_analyze_in
                , Frhash_header *p_st_hash_report)
{
    XML_PRINT_INFO("xml_analyze\n");
    L7_session *p_st_session = NULL;
    L7session_xml_private *p_st_session_private = NULL;
    L7_xml_packet *p_st_xml_packet = NULL;
    int i_unfragmented_ret = 0;
    int i_analyze_ret = APM_L7_ANALYZE_FAIL;                  // 本函数的返回值
    char arr_ip_src[56], arr_ip_dst[56];
    xml_detect_session *p_st_detect_sess = NULL;
    L7session_xml *p_st_sess = NULL;

    if(p_st_conn == NULL || p_analyze_in == NULL
      || p_st_hash_report == NULL)
        return APM_L7_ANALYZE_FAIL;

    p_st_session = frct_get_session(p_st_conn);
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_analyze_in->src_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_src), "%s", int_ntoa(p_analyze_in->dst_ip));

    XML_PRINT_TIMEVAL("PacketIn", p_analyze_in->st_timestamp);
    XML_PRINT_DBG("Src:[%s:%d] Dest:[%s:%d] SessionID:[%s]\n", arr_ip_src, p_analyze_in->src_port, arr_ip_dst, p_analyze_in->dst_port, p_st_session->arr_session_id);

    /*
    * 如果入力p_st_packet是FIN包, 说明本次connection结束,
    * 释放本次session信息. 如果入力p_st_packet是SYN包,说明新的
    * connection开始了, 释放上次可能残留的session信息
    */
    if (p_analyze_in->em_analyz_cmd == FRANALYZ_CMD_FIN || p_analyze_in->em_analyz_cmd == FRANALYZ_CMD_SYN)
    {
        i_analyze_ret = APM_L7_ANALYZE_SUCC;
        if (p_analyze_in->em_analyz_cmd == FRANALYZ_CMD_SYN)
            XML_PRINT_INFO("SYN PACKET\n");
        else if (p_analyze_in->em_analyz_cmd == FRANALYZ_CMD_FIN)
            XML_PRINT_INFO("FIN PACKET\n");

        if ((p_st_session_private = (L7session_xml_private *) frct_get_session_private(p_st_session)) != NULL)
        {
            p_st_sess = p_st_session_private->p_st_sess;
            p_st_xml_packet = p_st_session_private->p_st_xml_packet;

            if (p_st_sess && p_st_xml_packet && p_st_sess->hdr_type == APM_XML_HDR_TYPE_ZERO) // 纯XML
            {
                if (p_st_xml_packet->i_is_req == FR_YES)
                {
                    if (xml_req(p_st_conn, p_st_xml_packet, p_st_hash_report) == FR_SUCC)
                        i_analyze_ret = APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
                }
                else if (p_st_xml_packet->i_is_req == FR_NO)
                {
                    if (xml_resp(p_st_conn, p_st_xml_packet, p_st_hash_report) == FR_SUCC)
                        i_analyze_ret = APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
                }
            }

            xml_session_destory(p_st_session->p_session_private);
            p_st_session->p_session_private = NULL;
        }
        XML_PRINT("### i_analyze_ret:[%d]\n\n", i_analyze_ret);
        return i_analyze_ret;
    }

    // 本次链路的第一个有效packet包,创建链路
    if (p_st_session->p_session_private == NULL)
    {
        // 初始化xml的session数据
        if (__xml_private_session_init(p_st_session) != FR_SUCC)
            goto xml_analyze_fail;

        // 设置session uuid
        fruuid(p_st_session->arr_session_id);
    }

    // 打印方向
    switch (p_analyze_in->i_direction)
    {
        case FR_DIRECT_CLIENT2SRV:
            XML_PRINT("client["KLIMEGREEN"%s:%d"RESET"] to server["KLIMEGREEN"%s:%d"RESET"] ["KYELLOW"%d"RESET"] ->>>>>>>>>>>>>>>>>>>>>>>\n",
                      arr_ip_src,
                      p_analyze_in->src_port,
                      arr_ip_dst,
                      p_analyze_in->dst_port,
                      p_analyze_in->ui_payload_len);
            break;
        case FR_DIRECT_SRV2CLIENT:
            XML_PRINT("server["KLIMEGREEN"%s:%d"RESET"] to client["KLIMEGREEN"%s:%d"RESET"] ["KYELLOW"%d"RESET"] <<<<<<<<<<<<<<<<<<<<<<<-\n",
                      arr_ip_src,
                      p_analyze_in->src_port,
                      arr_ip_dst,
                      p_analyze_in->dst_port,
                      p_analyze_in->ui_payload_len);
            break;
        default:
            XML_PRINT("unknown direction, src["KLIMEGREEN"%s:%d"RESET"], dest["KLIMEGREEN"%s:%d"RESET"]\n",
                      arr_ip_src,
                      p_analyze_in->src_port,
                      arr_ip_dst,
                      p_analyze_in->dst_port);
            goto xml_analyze_fail;
    }

    p_st_session_private = (L7session_xml_private *) frct_get_session_private(p_st_session);
    p_st_sess = p_st_session_private->p_st_sess;
    p_st_xml_packet = p_st_session_private->p_st_xml_packet;

    if ((p_st_detect_sess = (xml_detect_session *) frct_detect_data_get(p_st_conn, MODULE_L7PROTO)) != NULL)
    {
        p_st_sess->hdr_type = p_st_detect_sess->hdr_type;
        p_st_sess->ui_hdr_flag_len = p_st_detect_sess->ui_hdr_flag_len;
        p_st_sess->i_hdrlen_from_head = p_st_detect_sess->i_hdrlen_from_head;
        frct_detect_hash_del(p_st_conn, MODULE_L7PROTO);
    }
    if (p_st_sess->hdr_type == APM_XML_HDR_TYPE_INIT || p_st_sess->hdr_type == APM_XML_HDR_TYPE_UNKNOWN)
    {
        XML_PRINT_CRIT("hdr_type:[%s] hdrlen_from_head:[%s]\n", XML_HDR_TYPE2STR(p_st_detect_sess->hdr_type),
                       p_st_detect_sess->i_hdrlen_from_head == FR_YES ? "YES" : "NO");
        goto xml_analyze_fail;
    }
    else if (p_st_sess->hdr_type == APM_XML_HDR_TYPE_ZERO) // 纯XML
    {
        i_unfragmented_ret = xml_packet_unfragment2(p_analyze_in, p_st_xml_packet);
        if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_NEED_NEXT_PACKET)
            goto xml_analyze_todo;
        else if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_REPORT_REQ)
        {
            if (xml_req(p_st_conn, p_st_xml_packet, p_st_hash_report) == FR_SUCC)
                i_analyze_ret = APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);

            xml_init_packet(p_st_xml_packet);
            i_unfragmented_ret = xml_packet_unfragment2(p_analyze_in, p_st_xml_packet);
            if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_NEED_NEXT_PACKET)
                goto xml_analyze_success;
            else if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_ERROR)
                goto xml_analyze_fail;
        }
        else if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_REPORT_RESP)
        {
            if (xml_resp(p_st_conn, p_st_xml_packet, p_st_hash_report) == FR_SUCC)
                i_analyze_ret = APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);

            xml_init_packet(p_st_xml_packet);
            i_unfragmented_ret = xml_packet_unfragment2(p_analyze_in, p_st_xml_packet);
            if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_NEED_NEXT_PACKET)
                goto xml_analyze_success;
            else if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_ERROR)
                goto xml_analyze_fail;
        }
        else if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_ERROR)
            goto xml_analyze_fail;
    }
    else
    {
        i_unfragmented_ret = xml_packet_unfragment(p_analyze_in, p_st_xml_packet, p_st_sess);
        if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_NEED_NEXT_PACKET)
            goto xml_analyze_todo;
        else if (i_unfragmented_ret == APM_XML_CHECK_FRAGMENT_ERROR)
            goto xml_analyze_fail;

        if (p_analyze_in->i_direction == FR_DIRECT_CLIENT2SRV)
        {
            if (xml_req(p_st_conn, p_st_xml_packet, p_st_hash_report) == FR_SUCC)
            {
                i_analyze_ret = APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
                goto xml_analyze_success;
            }
        }
        else
        {
            if (xml_resp(p_st_conn, p_st_xml_packet, p_st_hash_report) == FR_SUCC)
            {
                i_analyze_ret = APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
                goto xml_analyze_success;
            }
        }
    }

xml_analyze_todo:
    XML_PRINT("### i_analyze_ret todo:[%d]\n\n", APM_L7_ANALYZE_TODO);
    return APM_L7_ANALYZE_TODO;

xml_analyze_success:
    XML_PRINT("### i_analyze_ret success:[%d]\n\n", i_analyze_ret);
    return i_analyze_ret;

xml_analyze_fail:
    if (xml_packet_is_in_fragment(p_st_xml_packet))
    {
        if (bytebuff_setPosition(p_st_xml_packet->p_st_packet, 0) != FR_SUCC)
        {
            p_st_xml_packet->fragment_state = APM_XML_FRAGMENT_INIT;
            return APM_L7_ANALYZE_FAIL;
        }
        goto xml_analyze_todo;
    }
    else
    {
        XML_PRINT_PACKET("error packet", p_st_xml_packet);
        xml_init_packet(p_st_xml_packet);
    }

    XML_PRINT_EMERG("### i_analyze_ret fail:[%d]\n\n", APM_L7_ANALYZE_FAIL);
    return APM_L7_ANALYZE_FAIL;
}

/*******************************************************************************
*  FUNC     :  释放 L7_xml_packet
*  ARGS     :  **pp_st_xml_packet              (L7_xml_packet)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
FR_STATIC void __xml_destroy_packet(L7_xml_packet **pp_st_xml_packet)
{
    if (pp_st_xml_packet == NULL || *pp_st_xml_packet == NULL)
        return;

    L7_xml_packet *p_st_xml_packet = *pp_st_xml_packet;
    bytebuff_free(&(p_st_xml_packet->p_st_packet));
    xml_free((void **) pp_st_xml_packet);
    return;
}

/*******************************************************************************
*  FUNC     :  释放 L7_xml_stmt
*  ARGS     :  **pp_st_xml_stmt              (L7_xml_stmt)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
FR_STATIC void __xml_destroy_stmt(L7_xml_stmt **pp_st_xml_stmt)
{
    xml_free((void **) pp_st_xml_stmt);

    return;
}

int xml_session_destory(void *p_st_session_private_in)
{
    XML_PRINT(KBLUE "\nxml_session_destory run\n" RESET);
    if (p_st_session_private_in == NULL)
        return FR_SUCC;

    L7session_xml_private *p_st_private = (L7session_xml_private *) p_st_session_private_in;

    __xml_destroy_session(&(p_st_private->p_st_sess));

    __xml_destroy_packet(&(p_st_private->p_st_xml_packet));

    __xml_destroy_stmt(&(p_st_private->p_st_xml_stmt));

    frreport_body_l7list_free(&(p_st_private->p_report_comm));
    frreport_body_l7list_free(&(p_st_private->p_report_req));

    xml_free((void **) &p_st_private);

    XML_PRINT_INFO("leave xml_session_destory\n");
    return FR_SUCC;
}
